# -*- coding: utf-8 -*-
'''
一键Dump Memory ELF至A64Dbg与之对应的缓存目录。
'''
# https://gitee.com/geekneo/A64Dbg/blob/master/python3/adpdef.py
# 引入adp基础定义，比如error/event定义
from adpdef import *

# https://gitee.com/geekneo/A64Dbg/blob/master/python3/adp.py
# 引入adp所有python api
from adp import *

import os
import platform
import struct

# 开发模式则只使用源代码，不使用adc二进制模块
dev_mode = False

# 将unpack得到的字符串字节数组转为字符串
def simplify_strbuff(tmps):
    result = ''
    for c in tmps:
        if c > 0:
            result += chr(c)
        else:
            break
    return result

# 远程adcpp代码将会发送数据至该接口
# adcpp_elf_dump是adcpp api str2py/buf2py的第一个字符串参数，用于发送数据到该函数
'''
// 文件传输协议字段
struct dump_info {
  char filename[64];
  int filearch;
  int filesize;
  char filebuff[0];
};
'''
def adcpp_elf_dump(data):
    # ELF文件名称
    filename = simplify_strbuff(struct.unpack_from('64s', data, 0)[0])
    # ELF大小
    filearch_i = struct.unpack_from('i', data, 64)[0]
    if filearch_i:
        filearch = 'aarch64'
    else:
        filearch = 'arm'
    # ELF大小
    filesize = struct.unpack_from('i', data, 68)[0]
    # ELF数据
    filebuff = struct.unpack_from('%ds' % (filesize), data, 72)[0]
    print('Received %s, %s, %d.' % (filename, filearch, filesize))
    # 保存至A64Dbg与之对应的缓存目录
    usrdir = os.path.expanduser('~')
    cacheplat = 'android'
    if curPlatform() == adp_remote_vp_android:
        cacheplat = 'yoophone'
    cachedir = usrdir + '/A64Dbg/decache/%s/%s-linux-android' % (cacheplat, filearch)
    # 保存Dump后的ELF文件
    filepath = cachedir + '/dump-' + filename
    print('Saved to %s.' % (filepath))
    with open(filepath, 'wb') as fp:
        fp.write(filebuff)

# a64dbg插件入口
def adp_on_event(args):
    # 获取当前的事件码
    event = args[adp_inkey_type]
    # 用户执行了插件主菜单命令
    if event == adp_event_main_menu:
        # 判断当前目标平台是否是remote_uraniumvm_android
        plat = curPlatform()
        if plat == adp_remote_uraniumvm_android or \
            plat == adp_remote_vp_android:
            plugdir = os.path.dirname(__file__)
            # 需要用户输入内存ELF的基址
            elfbase = inputInteger('Input Memory ELF Base Address')
            if elfbase == 0 or not elfbase:
                print('内存ELF的基址不能为0.')
                return failed(adp_err_param)
            # yoophone
            if plat == adp_remote_vp_android:
                adcppfile = plugdir + '/adcpp-elf-dump-yoophone.adc'
            # aarch64
            elif elfbase > 0xFFFFFFFF:
                adcppfile = plugdir + '/adcpp-elf-dump64.adc'
            # arm
            else:
                adcppfile = plugdir + '/adcpp-elf-dump32.adc'
            if dev_mode:
                # 开发模式只使用源代码
                adcppfile = plugdir + '/adcpp-elf-dump.cc'
            if os.path.exists(adcppfile):
                # 将窗口焦点切换为Log窗口
                focusLog()
                # 将参数写入临时文件供adcpp_elf_dump.adc读取
                shell('echo 0x%x \\> /data/local/tmp/adcpp-elf-dump.txt' % (elfbase))
                # 在远程Android进程执行该脚本代码进行Dump操作
                # 需要预先利用远程Android虚拟化调试模式Attach目标进程，否则该脚本执行会报错
                runadc(adcppfile)
            else:
                print('Missing %s.' % (adcppfile))
            return success()
        else:
            print('adcpp-elf-dump插件只支持Android虚拟化调试模式Remote UraniumVM Android或者YooPhone Remote VP Android.')
            return failed(adp_err_unsupport)
    # 插件框架向插件询问主菜单名称
    if event == adp_event_menuname:
        return success('adcpp-elf-dump')
    # 插件框架向插件询问插件版本和简介
    if event == adp_event_adpinfo:
        return success(('0.1.1', "一键Dump Memory ELF至A64Dbg与之对应的缓存目录。"))
    return failed(adp_err_unimpl)
